サービス事業者向けリバースプロキシ フレームワーク Pingora ことはじめ
ども、大瀧です。
先日、Cloudflareが自社で利用しているソフトウェアPingoraをOSSとして公開しました。彼らはPingoraを"フレームワーク"と呼ぶようによくあるリバースプロキシとは建付けが異なるので、本ブログではPingoraがもたらすものとその特徴、最初の始め方をご紹介します。
Pingoraはフレームワーク
フレームワークというと、Ruby on RailsのようなWebアプリケーションフレームワークを連想すると思います。PingoraはWebアプリケーションではなくリバースプロキシやサービスゲートウェイのためのフレームワークです。
リバースプロキシというとNginxやHAProxyをイメージしますが、それらの完成されたリバースプロキシソフトウェアは最小限の構成ファイルで手軽に動作できる反面、他のシステムとの連携やパフォーマンスチューニングに独特な作法があったり、動的な構成変更に制約があります。
一方最近のプログラミング言語はリバースプロキシのためのライブラリを組み込みでサポートし、リバースプロキシソフトウェアをスクラッチで開発することが容易になっていますが、ライブラリの肥大化や依存関係、パフォーマンスなど超えるべき課題も多いです。
Pingoraは、リバースプロキシのよく利用する機能をRustのCrate(モジュールのようなもの)として提供するフレームワークです。Rustの豊富な Crateとの組み合わせや、ソフトウェアへの組み込みを前提とした柔軟な設計とCloudflareの実践的なプラクティスが盛り込まれた非同期処理などいわゆる「巨人の肩に乗る」いいとこ取りのリバースプロキシ開発ができるわけです。Pingoraの特徴やアーキテクチャはCloudflareのブログが詳しいです。
動かしてみよう
動作確認環境
- OS: macOS Sonoma バージョン 14.3.1
- Rust: バージョン 1.77.0
下準備
手元のMacにはRustがインストールされていなかったので、公式の手順に沿ってインストールします。また、Pingoraのビルドにはcmakeが必要なので、Homebrewであらかじめインストールしておきます(Homebrew自体のセットアップは割愛)。
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh $ brew install cmake
サンプルの実行
では、サンプルを動かしてみましょう。GitHubのリポジトリにサンプルがあるので、git pull
で持ってきます。
$ git clone https://github.com/cloudflare/pingora.git $ cd pingora/
Crateごとにたくさんのサンプルがありますが、READMEのCrateの説明に拠るとPingora
Crateのサンプルが良さそうです。
Pingora: the "public facing" crate to build networked systems and proxies
眺めてみた感じpingora/example/server.rs
がコマンドラインオプションやプロキシのひと通りを動かせそうなので、今回はこちらを試してみます。このサンプルでは、HTTPSおよびTLSで待ち受けるためのTLS証明書が以下のパスに配置されることを期待します。
- サーバー証明書:
./pingora/tests/keys/server.crt
- 秘密鍵:
./pingora/tests/keys/key.pem
mkcertなどであらかじめ作成しておきます。
$ mkdir pingora/tests/keys $ cd pingora/tests/keys $ mkcert "yakiniku.local" $ ln -s yakiniku.local.pem server.crt $ ln -s yakiniku.local-key.pem key.pem
これで証明書の準備ができました。では、実行してみましょう。
$ cargo run --release --package pingora --example server :(略) Compiling pingora-openssl v0.1.0 (/Users/xxxx/Repos/pingora/pingora-openssl) Compiling pingora-core v0.1.0 (/Users/xxxx/Repos/pingora/pingora-core) Compiling pingora v0.1.0 (/Users/xxxx/Repos/pingora/pingora) Finished release [optimized] target(s) in 1m 50s Running `target/release/examples/server` Usage port 6142: TCP echo server nc 127.0.0.1 6142 port 6143: TLS echo server openssl s_client -connect 127.0.0.1:6143 port 6145: Http echo server curl http://127.0.0.1:6145 -v -d 'hello' port 6148: Https echo server curl https://127.0.0.1:6148 -vk -d 'hello' port 6141: TCP proxy curl http://127.0.0.1:6141 -v -H 'host: 1.1.1.1' port 6144: TLS proxy curl https://127.0.0.1:6144 -vk -H 'host: one.one.one.one' -o /dev/null port 6150: metrics endpoint curl http://127.0.0.1:6150
この状態でもう一つのターミナルを開き、UsageにあるcURLコマンド例を試してみます。前半の4つは"echo server"とあるので、ターミナルからの入力やcURLの-d
オプションで文字列を送信すると、同じ文字列を返す実装になっていますね。TCP、HTTPおよびTLSをサポートする様子がわかります。
$ nc 127.0.0.1 6142 test test [Ctrl+C] $ curl http://127.0.0.1:6145 -v -d 'hello' * Trying 127.0.0.1:6145... * Connected to 127.0.0.1 (127.0.0.1) port 6145 > POST / HTTP/1.1 > Host: 127.0.0.1:6145 > User-Agent: curl/8.7.1 > Accept: */* > Content-Length: 5 > Content-Type: application/x-www-form-urlencoded > * upload completely sent off: 5 bytes < HTTP/1.1 200 OK < Content-Type: text/html < Content-Length: 5 < Date: Sat, 30 Mar 2024 22:32:24 GMT < Connection: keep-alive < * Connection #0 to host 127.0.0.1 left intact hello $
5,6個目のコマンド例はリバースプロキシの例です。それぞれ`https://1.1.1.1/`と`https://one.one.one.one/`へのHTTP/HTTPSリクエストと同じレスポンスボディが返ってくることが確認できます。
$ curl http://127.0.0.1:6141 -v -H 'host: 1.1.1.1' * Trying 127.0.0.1:6141... * Connected to 127.0.0.1 (127.0.0.1) port 6141 > GET / HTTP/1.1 > Host: 1.1.1.1 > User-Agent: curl/8.7.1 > Accept: */* > * Request completely sent off < HTTP/1.1 301 Moved Permanently < Server: cloudflare < Date: Sat, 30 Mar 2024 22:19:25 GMT < Content-Type: text/html < Content-Length: 167 < Connection: keep-alive < Location: https://1.1.1.1/ < CF-RAY: 86cb7b2cfd933511-NRT < <html> <head><title>301 Moved Permanently</title></head> <body> <center><h1>301 Moved Permanently</h1></center> <hr><center>cloudflare</center> </body> </html> * Connection #0 to host 127.0.0.1 left intact $
最後のコマンド例は、Prometheus Exporterへのリクエスト例です。サンプルではHTTP echo serverへのリクエスト数をメトリックとして取るようになっています。TCP echoやプロキシはカウントされないので注意しましょう。
$ curl http://127.0.0.1:6150 # HELP reg_counter Number of requests # TYPE reg_counter counter reg_counter 1 $
Prometheus対応はリバースプロキシ専用のExporterを扱うことが多かったので独自のメトリックを定義して作り込んでいくのは楽しそうな反面、統計情報を数える実装はいろいろ考えることが多そうで今からちょっとげんなりしている自分もいます(苦笑)。
気を取り直して、今回はサンプルを実行しましたが、ソースプログラムをイチから作るチュートリアルもあります。
また、今回触れなかったYAML形式の構成ファイルやSystemdのサービスへの登録などの解説、HTTPプロキシの実装ガイドなど情報満載のユーザーガイドもあります。現時点で作成中(WIP)となっているAdvanced topicsの項目がどれも面白そうで、追加されるのを楽しみにしています。
まとめ
リバースプロキシのフレームワークPingoraをご紹介しました。リバースプロキシを自作するケースはあまり多くないと思いますが、クラウドを活用するユーザーやインテグレーターの方も裏の仕組みを理解するために試しに触ってみるのも有用ではないでしょうか。プロキシをマルチテナントサービスとして提供するサービス事業者や大規模なKubernetes環境を運用する組織ではリバースプロキシの開発現場もあったりするので、興味があればそれらの話を聞いてみてもいいかもしれませんよ。